package com.szgd.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class ZipHelper {
	public static final Log logger = LogFactory.getLog(ZipHelper.class);
	private static boolean isCreateSrcDir = true;// 是否创建源目录
	private static final int bufferSize = 1024 * 1024;// 压缩文件写入流的缓冲大小
	
	public static void main(String[] args) {
		String fileName = "E:\\zkw2guidang\\case2016_X0031000020160816X01541";
		long startTime = System.currentTimeMillis();
		System.out.println("开始压缩:"+startTime);
		ZipHelper.createZip(fileName, fileName + ".zip", "By jiayb");
		
		long endTime = System.currentTimeMillis();
		
		long spendTime = endTime - startTime;
		
		System.out.println("本次压缩耗时：" + spendTime+"ms,"+ spendTime/ 1000 + "s" + spendTime /1000 /60 +"m");
		
		
		startTime = System.currentTimeMillis();
		System.out.println("开始解压:"+startTime);
		
		try {
			String unzipFilePath = "E:\\zkw2guidang\\"+startTime;
			ZipHelper.unzip(fileName + ".zip", unzipFilePath , false);
			
		} catch (ParameterException e) {
			e.printStackTrace();
		} catch (ZipException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		endTime = System.currentTimeMillis();
		
		spendTime = endTime - startTime;
		
		System.out.println("本次解压耗时：" + spendTime+"ms ,"+ spendTime/ 1000 + "s ");
	}
	
	/**
	 * 创建ZIP，利用Apache的工具包ant.jar
	 * 
	 * @param src
	 *            指定压缩源，可以是目录或文件
	 * @param archive
	 *            压缩包路径
	 * @param comment
	 *            压缩包注释
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static boolean createZip(String src, String archive, String comment) {
		try {
			// ----压缩文件：
			FileOutputStream f = new FileOutputStream(archive);
			// 使用指定校验和创建输出流
			CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32());

			ZipOutputStream zos = new ZipOutputStream(csum);
			// 支持中文
			//zos.setEncoding("GBK");
			BufferedOutputStream out = new BufferedOutputStream(zos);
			// 设置压缩包注释
			zos.setComment(comment);
			// 启用压缩
			zos.setMethod(ZipOutputStream.DEFLATED);
			// 压缩级别为默认，最强压缩为BEST_COMPRESSION，但时间要花得多一点
			
			zos.setLevel(Deflater.DEFAULT_COMPRESSION);

			File srcFile = new File(src);
			if (!srcFile.exists()
					|| (srcFile.isDirectory() && srcFile.list().length == 0)) {
				throw new FileNotFoundException(
						"File must exist and  ZIP file must have at least one entry.");
			}
			// 获取压缩源所在父目录
			src = src.replaceAll("\\\\", "/");
			String prefixDir = null;
			if (srcFile.isFile()) {
				prefixDir = src.substring(0, src.lastIndexOf("/") + 1);
			} else {
				prefixDir = (src.replaceAll("/$", "") + "/");
			}

			// 如果不是根目录
			if (prefixDir.indexOf("/") != (prefixDir.length() - 1)
					&& isCreateSrcDir) {
				prefixDir = prefixDir.replaceAll("[^/]+/$", "");
			}

			// 开始压缩
			writeRecursive(zos, out, srcFile, prefixDir);

			out.close();
			// 注：校验和要在流关闭后才准备，一定要放在流被关闭后使用
			// System.out.println("Checksum: " + csum.getChecksum().getValue());
			logger.info("压缩包["+archive+"]创建完成！");
		} catch (Exception e) {
			logger.error("压缩包["+archive+"]创建失败！异常信息："+e.getMessage());
			return false;
		}
		return true;
	}
	
	/**
	 * 递归压缩
	 * 
	 * 使用 org.apache.tools.zip.ZipOutputStream 类进行压缩，它的好处就是支持中文路径， 而Java类库中的
	 * java.util.zip.ZipOutputStream 压缩中文文件名时压缩包会出现乱码。 使用 apache 中的这个类与 java
	 * 类库中的用法是一新的，只是能设置编码方式了。
	 * 
	 * @param zos
	 * @param bo
	 * @param srcFile
	 * @param prefixDir
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static void writeRecursive(ZipOutputStream zos,
			BufferedOutputStream bo, File srcFile, String prefixDir)
			throws IOException, FileNotFoundException {
		ZipEntry zipEntry;

		String filePath = srcFile.getAbsolutePath().replaceAll("\\\\", "/")
				.replaceAll("//", "/");
		if (srcFile.isDirectory()) {
			filePath = filePath.replaceAll("/$", "") + "/";
		}
		String entryName = filePath.replace(prefixDir, "").replaceAll("/$", "");
		if (srcFile.isDirectory()) {
			if (!"".equals(entryName)) {
//				System.out.println("正在创建目录 - " + srcFile.getAbsolutePath()
//						+ "  entryName=" + entryName);

				// 如果是目录，则需要在写目录后面加上 /
				zipEntry = new ZipEntry(entryName + "/");
				zos.putNextEntry(zipEntry);
			}

			File srcFiles[] = srcFile.listFiles();
			for (int i = 0; i < srcFiles.length; i++) {
				writeRecursive(zos, bo, srcFiles[i], prefixDir);
			}
		} else {
//			System.out.println("正在写文件 - " + srcFile.getAbsolutePath()
//					+ "  entryName=" + entryName);
			BufferedInputStream bi = new BufferedInputStream(
					new FileInputStream(srcFile));

			// 开始写入新的ZIP文件条目并将流定位到条目数据的开始处
			zipEntry = new ZipEntry(entryName);
			zos.putNextEntry(zipEntry);
			byte[] buffer = new byte[bufferSize];
			int readCount = bi.read(buffer);

			while (readCount != -1) {
				bo.write(buffer, 0, readCount);
				readCount = bi.read(buffer);
			}
			// 注，在使用缓冲流写压缩文件时，一个条件完后一定要刷新一把，不
			// 然可能有的内容就会存入到后面条目中去了
			bo.flush();
			// 文件读完后关闭
			bi.close();
		}
	}
	
	/**
	 * 解压zip压缩包
	 * @param zipFilePath zip文件的全路径
	 * @param unzipFilePath 解压后的文件保存的路径
	 * @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名（true包含；false不包含）
	 * @throws Exception 
	 */
	public static void unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName) throws ParameterException, ZipException, IOException{
		if(StringUtils.isEmpty(zipFilePath) || StringUtils.isEmpty(unzipFilePath)){
			throw new ParameterException("参数为空");
		}
		File zipFile = new File(zipFilePath);
		//如果解压后的文件保存路径包含压缩文件的文件名，则追加该文件名到解压路径
		if(includeZipFileName){
			String fileName = zipFile.getName();
			if(StringUtils.isNotEmpty(fileName)){
				fileName = fileName.substring(0,fileName.lastIndexOf("."));
			}
			unzipFilePath = unzipFilePath + File.separator + fileName;
		}
		//创建解压缩文件保存的路径
		File unzipFileDir = new File(unzipFilePath);
		if(!unzipFileDir.exists() || !unzipFileDir.isDirectory()){
			unzipFileDir.mkdirs();
		}
		
		//开始解压
		ZipEntry entry = null;
		String entryFilePath = null , entryDirPath = null;
		File entryFile = null , entryDir = null;
		int index = 0 ,  count = 0 ,bufferSize = 1024;
		byte [] buffer = new byte[bufferSize];
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		ZipFile zip = new ZipFile(zipFile);
		Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
		//循环对压缩包里的每一个文件进行解压
		while(entries.hasMoreElements()){
			entry = entries.nextElement();
			//构建压缩包中一个文件解压后保存的文件全路径
			entryFilePath = unzipFilePath + File.separator + entry.getName();
			//构建解压后保存的文件夹路径
			entryFilePath = entryFilePath.replace("/", File.separator);
			index = entryFilePath.lastIndexOf(File.separator);
			if(index != -1){
				entryDirPath = entryFilePath.substring(0,index);
			}else{
				entryDirPath = "";
			}
			entryDir = new File(entryDirPath);
			//如果文件夹路径不存在，则创建;
			if(!entryDir.exists() || ! entryDir.isDirectory()){
				entryDir.mkdirs();
			}
			//创建解压文件，若是文件夹，直接跳过
			entryFile = new File(entryFilePath);
			if(entryFile.exists() && entryFile.isDirectory()){
				continue;
			}
			//写入文件
			bis = new BufferedInputStream(zip.getInputStream(entry));
			bos = new BufferedOutputStream(new FileOutputStream(entryFile));
			while((count = bis.read(buffer , 0 , bufferSize )) != -1 ){
				bos.write(buffer, 0 , count);
			}
			bis.close();
			bos.flush();
			bos.close();
			
		}
	}
}
